#!/usr/bin/env python
#
# Copyright 2007 Doug Hellmann.
#
"""
"""

__version__ = "$Id: models.py 149 2009-08-11 13:31:31Z doug.hellmann $"

# Import system modules
from django.core import validators
from django.core import urlresolvers
from django.core.exceptions import MultipleObjectsReturned
from django.db import models

import logging

# Import local modules


# Module

logger = logging.getLogger('docket.models')

class Book(models.Model):
    """A docket book.
    """

    year = models.IntegerField(core=True,
                               help_text='Four digit year (1800-3000)',
                               validator_list=[validators.NumberIsInRange(1800, 3000)],
                               )
    volume = models.PositiveIntegerField(core=True,
                                         help_text='Volume number, or 0 if none',
                                         )
    notes = models.TextField('Notes and description',
                             help_text='Enter a physical description of the book and other notes here.',
                             blank=True,
                             )

    class Meta:
        unique_together = ( ('year', 'volume'),
                            )
        ordering = ('year', 'volume')

    def __str__(self):
        return '%s/%s' % (self.year, self.volume)

    class Admin:
        pass


class Case(models.Model):
    """An entry in a docket book.
    """

    # where the case info is found
    book = models.ForeignKey(Book, core=True)
    page = models.PositiveIntegerField(core=True,
                                       help_text='The page number in the book')
    year = models.IntegerField(core=True,
                               help_text='Four digit year (1800-3000)',
                               validator_list=[validators.NumberIsInRange(1800, 3000)],
                               )
    # Cases are identified by number, but where there are multiple
    # cases with the same number a letter code is appended.
    num = models.CharField(max_length=8, core=True,
                           help_text='The case number',
                           )

    # when & where the case happened
    arrest_date = models.DateField(blank=True, null=True, help_text='YYYY-MM-DD')
    arrest_location = models.CharField(max_length=256, blank=True,
                                       help_text='Stree address or general description')

    # when the case was heard
    hearing_date = models.DateField(blank=True, null=True, help_text='YYYY-MM-DD')

    # how the defendant pled
    PLEA_CHOICES = ( ('G', 'Guilty'),
                     ('NG', 'Not Guilty'),
                     ('NC', 'No contest'),
                     ('S', 'Suspended'),
                     ('U', 'Unknown'),
                     )
    plea = models.CharField(max_length=2, choices=PLEA_CHOICES, default='G')

    # how the judge ruled
    OUTCOME_CHOICES = ( ('G', 'Guilty'),
                        ('D', 'Dismissed'),
                        ('NG', 'Not Guilty'),
                        ('U', 'Unknown'),
                        )
    outcome = models.CharField(max_length=2, choices=OUTCOME_CHOICES, default='G')

    # Other notes that don't fit into any of the columns
    notes = models.TextField(blank=True)

    # Should this case show up on the home page?
    featured = models.BooleanField(default=False)

    # Logging info
    entered = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)
    
    class Admin:

        fields = (
            ('Identity', {'fields':('book', 'page', 'year', 'num'),
                          },
             ),
            ('Arrest', {'fields':('arrest_date', 'arrest_location'),
                        },
             ),
            ('Hearing', {'fields':('hearing_date', 'plea', 'outcome'),
                         },
             ),
            ('Other', {'fields':('notes', 'featured', 'modified'),
                       },
             ),
            )

        list_display = (  '__str__', 'book', 'page', 'arrest_date', 
                          'hearing_date', 
                          'defendant', 
                          'violation_codes',
                         'featured', 'modified')
        list_filter = ( 'year', 'book', 'page', 'featured')

        #date_hierarchy = 'hearing_date'

    class Meta:
        unique_together = ( ('year', 'num'),
                            )
        get_latest_by = 'modified'
        #order_with_respect_to = 'book'
        ordering = ('year', 'num')

    def __str__(self):
        return '%s/%s' % (self.year, self.num)

    def defendant(self):
        """Return the participant with type-code indicating they are the defendant."""
        try:
            return Participant.objects.get(case=self, role__name='Defendant')
        except MultipleObjectsReturned:
            return None

    def violation_codes(self):
        """Return a string with the violation codes for the case."""
        return ', '.join([ str(v) 
                           for v in self.violation_set.all().order_by('id') 
                           ])

    def sentence_rendered(self):
        """Return all of the rendered or contempt sentences for a case."""
        all = { }
        for sent in self.sentence_set.filter(kind__in=('R', 'C')).order_by('date'):
            if sent.kind == 'C' or sent.amount_kind == 'C':
                all.setdefault('required', []).append(sent)
            else:
                all.setdefault('options', []).append(sent)
        return all

    def sentence_served(self):
        """Return all of the sentences served for a case."""
        return self.sentence_set.filter(kind='S').order_by('date')

    def get_absolute_url(self):
        url = urlresolvers.reverse('docket.views.case',
                                   kwargs={'year':self.year,
                                            'casenum':self.num,
                                           })
        logger.debug('URL for Case %s/%s: %s', self.year, self.num, url)
        return url


class ViolationCode(models.Model):
    """What a Violation code means"""
    code = models.CharField(max_length=32, core=True, unique=True)
    summary = models.CharField(max_length=256, blank=True)
    long_description = models.TextField(blank=True)

    class Admin:
        list_display = (  'code', 'summary' )
    class Meta:
        ordering = ('code',)

    def __str__(self):
        if self.summary:
            return '%s (%s)' % (self.code, self.summary)
        else:
            return str(self.code)


class Violation(models.Model):
    """What the case is for.
    """
    case = models.ForeignKey(Case, edit_inline=models.TABULAR, num_in_admin=1)
    code = models.ForeignKey(ViolationCode, core=True)
    notes = models.TextField(blank=True)

    def __str__(self):
        if self.code.code:
            return str(self.code)
        else:
            return str(self.notes)


class Role(models.Model):
    """The way a person is involved in a case (e.g., Judge, Defendant, etc.).
    """

    name = models.CharField(max_length=64, core=True)
    display_order = models.IntegerField(default=1)

    class Admin:
        list_display = (  'name', 'display_order' )

    class Meta:
        ordering = ('display_order',)

    def __str__(self):
        return self.name

class Participant(models.Model):
    """A person involved in the case.
    """

    case = models.ForeignKey(Case, edit_inline=models.TABULAR, num_in_admin=10)
    role = models.ForeignKey(Role)
    first_name = models.CharField(max_length=128, blank=True)
    middle_name = models.CharField(max_length=128, blank=True)
    last_name = models.CharField(max_length=128, core=True,
                                 help_text='Name or "Vehicle"')
    license_plate = models.CharField(max_length=32, blank=True)
    alias = models.CharField(max_length=128, blank=True)
    title = models.CharField(max_length=8, blank=True)
    suffix = models.CharField(max_length=8, blank=True)
    race = models.CharField(max_length=8, blank=True)
    GENDER_CHOICES = ( ('M', 'Male'),
                       ('F', 'Female'),
                       )
    gender = models.CharField(max_length=1, blank=True, choices=GENDER_CHOICES,
                              default='')
    notes = models.TextField(blank=True)

    def name(self):
        """Return just the name of the participant.

        The name is composed of available parts in their natural order:

        title first_name middle_name last_name
        """
        if self.last_name.lower() == 'vehicle':
            return 'Vehicle: %s' % self.license_plate
        name_parts = [ self.title, self.first_name, 
                       self.middle_name, self.last_name]
        if self.alias:
            name_parts.append('(%s)' % self.alias)
        full_name = ' '.join([p for p in name_parts if p])
        return full_name

    def sortable_name(self):
        """Return the full name formatted to make sorting more natural.

        The name is composed of available parts, with the last name at
        the front.

        last_name, first_name middle_name
        """
        if self.last_name.lower() == 'vehicle':
            return 'Vehicle: %s' % self.license_plate
        full_name = self.last_name

        name_parts = [ self.first_name, self.middle_name ]
        if self.alias:
            name_parts.append('(%s)' % self.alias)
        
        remaining_name = ' '.join([p for p in name_parts if p])
        if remaining_name:
            full_name = full_name + ', ' + remaining_name

        return full_name

    def __str__(self):
        return '%s (%s)' % (self.name(), self.role)

    
class MediaFile(models.Model):
    """Media files associated with the case (e.g., scanned newspaper articles).
    """

    case = models.ForeignKey(Case, edit_inline=models.TABULAR, num_in_admin=1)
    title = models.CharField(core=True, max_length=512)
    citation = models.CharField(core=True, max_length=256)
    image = models.ImageField(upload_to='case_images')

    class Admin:
        list_display = ('case', 'title', 'citation', 'image')

    def __str__(self):
        return '%s (%s)' % (self.title, self.citation)


class Sentence(models.Model):
    """The sentence handed down or served in the case.
    """

    case = models.ForeignKey(Case, edit_inline=models.TABULAR, num_in_admin=4)

    # How is the sentence treated
    KIND_CHOICES = ( ('R', 'Rendered'),
                     ('S', 'Served'),
                     ('C', 'Contempt'),
                     )
    kind = models.CharField(max_length=1, choices=KIND_CHOICES, default='R')

    # How much value is associated with the sentence,
    # and what type of value is it?
    amount = models.FloatField(core=True)
    AMOUNT_CHOICES = ( ('F', 'Fine'),
                       ('J', 'Days in jail'),
                       ('L', 'Days of labor'),
                       ('M', 'Months of labor'),
                       ('W', 'Days of labor'),
                       ('C', 'Cost'),
                       ('P', 'Paid'),
                       ('O', 'Other'),
                       ('R', 'Remitted by Mayor'),
                       )
    amount_kind = models.CharField(max_length=1, choices=AMOUNT_CHOICES)

    # When was the sentence handed down or served?
    date = models.DateField(blank=True)

    # Misc. notes
    note = models.CharField(max_length=512, blank=True)
    
    class Admin:
        pass

    def amount_is_money(self):
        """Return boolean indicating whether or not the amount represents money."""
        return self.amount_kind in ('F', 'C', 'P')

    def __str__(self):
        parts = [ self.get_kind_display() ]
        if self.amount_is_money():
            parts.append('$%0.02f' % self.amount)
        else:
            parts.append(str(self.amount))
        parts.append(self.get_amount_kind_display())
        return ' '.join(parts)

        
